package com.example.boot.service.transaction.impl;

import com.example.boot.domain.User;
import com.example.boot.mapper.simple_test.UserMapper;
import com.example.boot.service.transaction.ITransactionRequiresNewService;
import com.example.boot.service.transaction.ITransactionService1;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;

/**
 * PROPAGATION_REQUIRES_NEW
 */
@Service
public class TransactionRequiresNewServiceImpl implements ITransactionRequiresNewService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private ITransactionService1 iTransactionService1;


    /**
     * 2.1 场景一
     * 外围方法没有开启事务。
     * 验证方法1：
     * 张三李四均插入
     */
    @Override
    public void notransaction_exception_requiresNew_requiresNew() {
        User User = new User();
        User.setName("张三");
        this.addRequiresNew1(User);

        User user2 = new User();
        user2.setName("李四");
        this.addRequiresNew2(user2);
        throw new RuntimeException();
    }

    /**
     * 2.1 场景一
     * 外围方法没有开启事务。
     * 验证方法2：
     */
    @Override
    public void notransaction_requiresNew_requiresNew_exception() {
        User User = new User();
        User.setName("张三");
        this.addRequiresNew1(User);

        User user2 = new User();
        user2.setName("李四");
        // 里层方法与外层方法在同一个类，里面事务不生效，此时即使里层方法报错，张三李四均插入
        // this.addRequiresNewException(user2);

        // 生效,此方法即使报错，张三插入，李四不插入
        ((ITransactionRequiresNewService) AopContext.currentProxy()).addRequiresNewException(user2);
        // ITransactionRequiresNewService cast = ITransactionRequiresNewService.class.cast(AopContext.currentProxy());
        // cast.addRequiresNewException(user2);

        // 生效,此方法即使报错，张三插入，李四不插入
        // iTransactionService1.addRequiresNewException(user2);
    }

    /**
     * 2.2 场景二
     * 外围方法开启事务。
     * 验证方法1：
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_exception_required_requiresNew_requiresNew() {
        User User = new User();
        User.setName("张三");
        // “张三”未插入
        this.addRequired(User);

        User user2 = new User();
        user2.setName("李四");
        // “李四”未插入，事务不生效
        // this.addRequiresNew1(user2);
        // “李四”插入，事务生效
        ((ITransactionRequiresNewService) AopContext.currentProxy()).addRequiresNew1(user2);

        User user3 = new User();
        user3.setName("王五");
        // “王五”未插入
        // this.addRequiresNew2(user3);
        // “王五”插入，事务生效
        ((ITransactionRequiresNewService) AopContext.currentProxy()).addRequiresNew1(user3);

        throw new RuntimeException();
    }

    /**
     * 2.2 场景二
     * 外围方法开启事务。
     * 验证方法2：
     * “张三”未插入，“李四”插入，“王五”未插入。
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_requiresNew_requiresNew_exception() {
        User User = new User();
        User.setName("张三");
        this.addRequired(User);

        User user2 = new User();
        user2.setName("李四");
        // this.addRequiresNew1(user2);
        ((ITransactionRequiresNewService) AopContext.currentProxy()).addRequiresNew1(user2);

        com.example.boot.domain.User user3 = new User();
        user3.setName("王五");
        // this.addRequiresNewException(user3);
        ((ITransactionRequiresNewService) AopContext.currentProxy()).addRequiresNewException(user2);
    }

    /**
     * 2.2 场景二
     * 外围方法开启事务。
     * 验证方法3：
     * “张三”插入，“李四”插入，“王五”未插入。
     *
     * 外围方法开启事务，插入“张三”方法和外围方法一个事务，插入“李四”方法、插入“王五”方法分别在独立的新建事务中。
     * 插入“王五”方法抛出异常，首先插入“王五”方法的事务被回滚，异常被catch不会被外围方法感知，外围方法事务不回
     * 滚，故插入“张三”方法插入成功。
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_requiresNew_requiresNew_exception_try() {
        User User = new User();
        User.setName("张三");
        this.addRequired(User);

        User user2 = new User();
        user2.setName("李四");
        // this.addRequiresNew1(user2);
        ((ITransactionRequiresNewService) AopContext.currentProxy()).addRequiresNew1(user2);


        User user3 = new User();
        user3.setName("王五");
        try {
            // addRequiresNewException(user3);
            ((ITransactionRequiresNewService) AopContext.currentProxy()).addRequiresNewException(user3);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            System.out.println("回滚");
        }
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew1(User user) {
        userMapper.insert(user);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(User user) {
        userMapper.insert(user);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew2(User user) {
        userMapper.insert(user);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNewException(User user) {
        userMapper.insert(user);
        throw new RuntimeException();
    }
}
